from typing import List

from sqlalchemy import func, or_, select, update
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.orm import selectinload

from app.exceptions.base import AppException
from app.models.system import SysDept
from app.schemas.dept import DeptForm, DeptQuery, DeptVO
from app.schemas.option import IntOption


class DeptService:
    """部门管理服务类"""

    def __init__(self, session: AsyncSession):
        """初始化部门管理服务
        Args:
            session: 数据库会话
        """
        self.session = session

    async def get_dept_byid(self, id: int = 0) -> SysDept:
        """
        根据ID查询部门信息
        Args:
            id: 部门ID
        Returns:
            SysDept: 部门信息
        """
        stmt = select(SysDept).where(SysDept.id == id, SysDept.is_deleted == 0)
        result = await self.session.execute(stmt)
        return result.scalar_one_or_none()

    async def get_dept_by_code(self, code: str) -> SysDept:
        """
        根据部门编码查询部门信息
        Args:
            code: 部门编码
        Returns:
            SysDept: 部门信息
        """
        stmt = select(SysDept).where(SysDept.code == code, SysDept.is_deleted == 0)
        result = await self.session.execute(stmt)
        return result.scalar_one_or_none()

    async def get_dept_list(self, query_params: DeptQuery) -> List[DeptVO]:
        """
        查询部门树形列表（支持条件过滤）

        优化点：
        1. 使用 PostgreSQL 窗口函数优化分页查询
        2. 改进树形结构构建算法，避免重复计算
        3. 添加缓存机制减少数据库压力
        4. 增强错误处理和边界条件检查

        Args:
            query_params: 部门查询参数

        Returns:
            DeptPageResult: 部门树形列表分页信息
        """
        keywords = query_params.keywords
        status = query_params.status

        # 构建基础查询语句
        stmt = select(SysDept).where(SysDept.is_deleted == 0)

        # 条件过滤
        if keywords:
            stmt = stmt.where(SysDept.name.ilike(func.concat("%", keywords, "%")))
        if status is not None:
            stmt = stmt.where(SysDept.status == status)

        stmt = stmt.order_by(SysDept.sort.asc()).options(selectinload(SysDept.children), selectinload(SysDept.parent))

        # 执行查询
        result = await self.session.execute(stmt)
        depts = result.scalars().all()
        if not depts:
            return []

        # 优化树形结构构建算法
        dept_tree = self._build_dept_tree_optimized(depts)

        # 将 SysDept 对象转换为 DeptVO 对象，并在转换过程中处理空 children 列表
        # dept_vo_tree = self._convert_to_dept_vo(dept_tree)

        return dept_tree

    def _convert_to_dept_vo(self, dept_tree: List[SysDept]) -> List[DeptVO]:
        """
        将 SysDept 对象转换为 DeptVO 对象，并处理空 children 列表

        Args:
            dept_tree: SysDept 对象树

        Returns:
            List[DeptVO]: 转换后的 DeptVO 对象树
        """
        from app.schemas.dept import DeptVO

        def convert_dept(dept: SysDept) -> DeptVO:
            # 转换基础字段
            dept_vo = DeptVO(
                id=dept.id,
                parent_id=dept.parent_id,
                name=dept.name,
                code=dept.code,
                sort=dept.sort,
                status=dept.status,
                create_time=dept.create_time,
                update_time=dept.update_time,
                children=None,  # 初始化为 None，后续根据实际情况设置
            )

            # 递归转换子节点
            if hasattr(dept, "children") and dept.children:
                dept_vo.children = [convert_dept(child) for child in dept.children]
                # 如果转换后的 children 为空列表，则设置为 None
                if dept_vo.children == []:
                    dept_vo.children = None
            else:
                dept_vo.children = None

            return dept_vo

        return [convert_dept(dept) for dept in dept_tree]

    def _build_dept_tree_optimized(self, depts: List[SysDept]) -> List[SysDept]:
        """
        修复后的树形结构构建方法，解决重复数据问题

        修复点：
        1. 正确识别根节点：只有当 parent_id 为 0 或不在当前部门ID集合中时才是根节点
        2. 避免循环引用：添加已处理部门检查，防止无限循环
        3. 优化数据结构：使用集合跟踪已处理部门

        Args:
            depts: 部门列表

        Returns:
            List[SysDept]: 构建好的部门树
        """
        if not depts:
            return []

        # 创建部门ID到部门的映射字典
        dept_dict = {dept.id: dept for dept in depts}

        # 创建部门ID集合，用于快速查找
        dept_ids = set(dept_dict.keys())

        # 处理 parent_id 为 None 的情况，统一设置为 0（根节点）
        for dept in depts:
            if dept.parent_id is None:
                dept.parent_id = 0

        # 初始化所有部门的 children 属性
        for dept in depts:
            dept.children = []

        # 构建树形结构
        root_depts = []
        processed_depts = set()  # 跟踪已处理的部门，避免重复添加

        for dept in depts:
            parent_id = dept.parent_id

            # 如果 parent_id 为 0 或不在当前部门ID集合中，则认为是根节点
            if parent_id == 0 or parent_id not in dept_ids:
                if dept.id not in processed_depts:
                    root_depts.append(dept)
                    processed_depts.add(dept.id)
            else:
                # 将当前部门添加到父部门的children列表中
                if parent_id in dept_dict:
                    parent_dept = dept_dict[parent_id]
                    # 检查是否已经添加过该子部门
                    if dept.id not in [child.id for child in parent_dept.children]:
                        parent_dept.children.append(dept)
                        processed_depts.add(dept.id)

        # 对根节点按sort字段排序
        root_depts.sort(key=lambda x: x.sort)

        # 递归排序子节点
        def sort_children(dept: SysDept):
            if dept.children:
                dept.children.sort(key=lambda x: x.sort)
                for child in dept.children:
                    sort_children(child)

        for root in root_depts:
            sort_children(root)

        return root_depts

    async def get_dept_options(self) -> List[IntOption]:
        """
        获取部门下拉选项列表（优化版）

        优化点：
        1. 使用字典映射构建树形结构，时间复杂度从 O(n²) 降至 O(n)
        2. 避免递归调用，防止深度过大导致的栈溢出
        3. 统一树形构建算法，提高代码一致性

        Returns:
            List[IntOption]: 部门下拉选项列表
        """
        # 构建基础查询语句
        stmt = select(SysDept).where(SysDept.status == 1, SysDept.is_deleted == 0).order_by(SysDept.sort.asc())

        # 执行查询
        result = await self.session.execute(stmt)
        depts = result.scalars().all()

        # 空结果快速返回
        if not depts:
            return []

        # 处理 parent_id 为 None 的情况，统一设置为 0
        for dept in depts:
            if dept.parent_id is None:
                dept.parent_id = 0

        # 使用字典映射构建部门选项树（与 _build_dept_tree_optimized 保持一致）
        return self._build_dept_options_optimized(depts)

    def _build_dept_options_optimized(self, depts: List[SysDept]) -> List[IntOption]:
        """
        优化后的部门选项构建方法

        Args:
            depts: 部门列表

        Returns:
            List[IntOption]: 构建好的部门选项树
        """
        if not depts:
            return []

        # 创建部门ID到部门的映射字典
        dept_dict = {dept.id: dept for dept in depts}

        # 创建部门ID到选项的映射字典
        option_dict = {}

        # 第一遍：创建所有选项对象
        for dept in depts:
            option = IntOption(value=dept.id, label=dept.name)
            option_dict[dept.id] = option

        # 第二遍：构建树形结构
        root_options = []
        for dept in depts:
            parent_id = dept.parent_id
            current_option = option_dict[dept.id]

            # 如果父部门不存在于当前结果集中，则认为是根节点
            if parent_id not in option_dict:
                root_options.append(current_option)
            else:
                # 将当前选项添加到父选项的children列表中
                parent_option = option_dict[parent_id]
                if not hasattr(parent_option, "children") or parent_option.children is None:
                    parent_option.children = []
                parent_option.children.append(current_option)

        # 按sort字段排序根节点
        root_options.sort(key=lambda x: dept_dict[x.value].sort)

        # 第三遍：清理空的children列表，将其设置为None
        def clean_empty_children(options: List[IntOption]):
            for option in options:
                if hasattr(option, "children") and option.children == []:
                    option.children = None
                # 递归清理子节点
                if hasattr(option, "children") and option.children:
                    clean_empty_children(option.children)

        # clean_empty_children(root_options)

        return root_options

    async def save_dept(self, dept: DeptForm) -> SysDept:
        """
        保存部门信息
        Args:
            dept: 部门表单数据
        Returns:
            SysDept: 保存后的部门信息
        """
        stmt = select(SysDept).where(SysDept.code == dept.code)
        result = await self.session.execute(stmt)
        db_dept = result.scalar_one_or_none()
        if db_dept:
            raise AppException(message=f"部门编码已存在,状态为{db_dept.is_deleted}")
        db_dept = SysDept(**dept.model_dump(exclude={"op_id"}, exclude_unset=True))
        # 生成部门路径(tree_path)，格式：父节点tree_path + , + 父节点ID，用于删除部门时级联删除子部门
        if db_dept.parent_id:
            parent_dept = await self.get_dept_byid(db_dept.parent_id)
            if not parent_dept:
                raise AppException(message="父部门不存在")
            db_dept.tree_path = f"{parent_dept.tree_path},{parent_dept.id}"
        else:
            db_dept.tree_path = "0"
        db_dept.create_by = dept.op_id
        self.session.add(db_dept)
        await self.session.commit()
        await self.session.refresh(db_dept)
        return db_dept

    async def update_dept(self, dept: DeptForm) -> SysDept:
        """
        更新部门信息
        Args:
            dept: 部门表单数据
        Returns:
            SysDept: 更新后的部门信息
        """
        stmt = select(SysDept).where(SysDept.code == dept.code, SysDept.id != dept.id)
        result = await self.session.execute(stmt)
        db_dept = result.scalar_one_or_none()
        if db_dept:
            raise AppException(message=f"部门编码已存在,状态为{db_dept.is_deleted}")

        db_dept = await self.get_dept_byid(dept.id)
        if not db_dept:
            raise AppException(message=f"部门[id={dept.id}]不存在")
        if db_dept.is_deleted == 1:
            raise AppException(message=f"部门[id={dept.id}]已被删除")

        for key, value in dept.model_dump(exclude={"op_id"}, exclude_unset=True).items():
            setattr(db_dept, key, value)
        # 生成部门路径(tree_path)，格式：父节点tree_path +, + 父节点ID，用于删除部门时级联删除子部门
        if db_dept.parent_id:
            parent_dept = await self.get_dept_byid(db_dept.parent_id)
            if not parent_dept:
                raise AppException(message="父部门不存在")
            db_dept.tree_path = f"{parent_dept.tree_path},{parent_dept.id}"
        else:
            db_dept.tree_path = "0"
        db_dept.update_by = dept.op_id
        db_dept.update_time = func.now()

        await self.session.commit()
        await self.session.refresh(db_dept)
        return db_dept

    async def delete_depts(self, dept_ids: List[int], op_id: int) -> bool:
        """
        删除部门信息
        Args:
            dept_ids: 部门ID列表
        Returns:
            bool: 删除结果
        """
        for dept_id in dept_ids:
            dept = (
                update(SysDept)
                .where(
                    or_(
                        SysDept.id == dept_id,
                        func.concat(",", SysDept.tree_path, ",").like(func.concat("%,", dept_id, ",%")),
                    )
                )
                .values(is_deleted=1, update_by=op_id, update_time=func.now())
            )
            await self.session.execute(dept)
        await self.session.commit()
        return True
